package easycloud.ecs.provider.api.aliyun;

import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.aliyun.openservices.ClientException;
import com.aliyun.openservices.oss.OSSClient;
import com.aliyun.openservices.oss.OSSErrorCode;
import com.aliyun.openservices.oss.OSSException;
import com.aliyun.openservices.oss.model.ListObjectsRequest;
import com.aliyun.openservices.oss.model.OSSObject;
import com.aliyun.openservices.oss.model.OSSObjectSummary;
import com.aliyun.openservices.oss.model.ObjectListing;
import com.aliyun.openservices.oss.model.ObjectMetadata;
import com.aliyun.openservices.oss.model.PutObjectResult;

import easycloud.ecs.common.domain.Folder;
import easycloud.ecs.common.domain.Item;
import easycloud.ecs.common.domain.Log.LOG_TYPE;
import easycloud.ecs.common.domain.Provider;
import easycloud.ecs.common.domain.Sync;
import easycloud.ecs.provider.api.IProvider;
import easycloud.ecs.provider.api.ProviderException;
import easycloud.ecs.provider.model.IStorageObject;
import easycloud.ecs.provider.model.IStorageObjectMetadata;
import easycloud.ecs.provider.model.aliyun.AliyunStorageObject;
import easycloud.ecs.provider.model.aliyun.AliyunStorageObjectMetadata;
import easycloud.ecs.provider.service.SendLogService;

public class AliyunOSSProvider implements IProvider {

	private SendLogService sendLogService;

	private Provider config;

	private static OSSClient adapter;

	private boolean isReady = false;

	private static final Log logger = LogFactory
			.getLog(AliyunOSSProvider.class);

	private static String BUCKET_NAME;

	public AliyunOSSProvider(Provider config) {
		this.config = config;
		AliyunOSSProvider.adapter = new OSSClient(this.config.getAccessKey(),
				this.config.getSecretKey());
		AliyunOSSProvider.BUCKET_NAME = this.config.getBucketName();
	}

	/**
	 * 
	 */
	@Override
	public boolean delete(Item item) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		try {
			AliyunOSSProvider.adapter.deleteObject(
					AliyunOSSProvider.BUCKET_NAME, item.getPath());
			result = true;
			AliyunOSSProvider.logger.info(getName() + " delete OK: "
					+ item.getPath());
		} catch (Exception e) {
			AliyunOSSProvider.logger.warn(getName() + " delete failed", e);
			throw new ProviderException(getName() + " delete failed", e,
					item.getPath());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.DELETE);
			log.setCreateTime(new Date());
			log.setResult(result);
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		return result;
	}

	@Override
	public URL getAccessURL(Item item) {
		Date expiration = new Date(System.currentTimeMillis()
				+ (1000 * 60 * 10));
		URL url = null;
		try {
			url = AliyunOSSProvider.adapter.generatePresignedUrl(
					AliyunOSSProvider.BUCKET_NAME, item.getPath(), expiration);
		} catch (ClientException e) {
			AliyunOSSProvider.logger.warn("generate access url Failed: "
					+ e.getMessage());
		}
		return url;
	}

	/**
	 * 
	 * @param object
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObject getItem(Item item) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		OSSObject object = null;
		try {
			object = AliyunOSSProvider.adapter.getObject(
					AliyunOSSProvider.BUCKET_NAME, item.getPath());
			AliyunOSSProvider.logger.info(getName() + " getFile OK: "
					+ item.getPath());

			result = true;
		} catch (Exception e) {
			AliyunOSSProvider.logger.warn(getName() + " getFile failed", e);
			throw new ProviderException(getName() + " getFile failed", e,
					item.getPath());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.GET);
			if (object != null) {
				log.setLength(object.getObjectMetadata().getContentLength());
			}
			log.setCreateTime(new Date());
			log.setResult(result);
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		return new AliyunStorageObject(object, item);
	}

	private String getLogicalPath(OSSObjectSummary summary, Folder folder) {
		String prefix = getPhysicalPath(folder);
		return StringUtils.replaceOnce(summary.getKey(), prefix,
				folder.getName());
	}

	@Override
	public String getName() {
		return "AliyunOSS";
	}

	private String getPhysicalPath(Folder folder) {
		for (Sync sync : config.getSyncs()) {
			if (sync.getFolder().equals(folder)) {
				return sync.getPath();
			}
		}
		return "";
	}

	/**
	 * 
	 */
	@Override
	public IStorageObjectMetadata head(Item item) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		ObjectMetadata result = null;
		try {
			result = AliyunOSSProvider.adapter.getObjectMetadata(
					AliyunOSSProvider.BUCKET_NAME, item.getPath());
			AliyunOSSProvider.logger.info(getName() + " head OK: "
					+ item.getPath());
		} catch (OSSException e) {
			if (OSSErrorCode.NO_SUCH_KEY.equals(e.getErrorCode())) {
				return null;
			}
		} catch (Exception e) {
			AliyunOSSProvider.logger.warn(getName() + " head failed", e);
			throw new ProviderException(getName() + " head failed", e,
					item.getPath());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.HEAD);
			log.setCreateTime(new Date());
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		if (result != null) {
			return new AliyunStorageObjectMetadata(result, item);
		} else {
			return null;
		}
	}

	/**
	 * 
	 * @param folder
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public List<IStorageObjectMetadata> list(Folder folder)
			throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		ObjectListing listing = null;
		try {
			String physicalPath = getPhysicalPath(folder);
			ListObjectsRequest request = new ListObjectsRequest(
					AliyunOSSProvider.BUCKET_NAME);
			request.setPrefix(physicalPath);
			listing = AliyunOSSProvider.adapter.listObjects(request);
			AliyunOSSProvider.logger.info(getName() + " list OK: "
					+ folder.getName());
		} catch (Exception e) {
			AliyunOSSProvider.logger.warn(getName() + " list failed", e);
			throw new ProviderException(getName() + " list failed", e,
					folder.getName());
		} finally {
			easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
			log.setLogType(LOG_TYPE.LIST);
			log.setCreateTime(new Date());
			log.setProvider(config);
			sendLogService.addLog(log);
		}
		if (listing != null) {
			List<IStorageObjectMetadata> result = new ArrayList<IStorageObjectMetadata>();
			List<OSSObjectSummary> adapterResult = listing.getObjectSummaries();
			for (OSSObjectSummary summary : adapterResult) {
				Item item = new Item();
				String path = getLogicalPath(summary, folder);
				item.setPath(path);
				item.setFolder(folder);
				result.add(new AliyunStorageObjectMetadata(summary, item));
			}
			return result;
		} else {
			return null;
		}
	}

	/**
	 * Upload file
	 * 
	 * @param file
	 * @param object
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObjectMetadata putItem(Item item, InputStream is, long length)
			throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		PutObjectResult putResult = null;
		if (length > 0) {
			final ObjectMetadata metadata = new ObjectMetadata();
			metadata.setContentLength(length);
			try {

				putResult = AliyunOSSProvider.adapter.putObject(
						AliyunOSSProvider.BUCKET_NAME, item.getPath(), is,
						metadata);
				AliyunOSSProvider.logger.info(getName() + " putFile OK: "
						+ item.getPath());
				result = true;
			} catch (Exception e) {
				AliyunOSSProvider.logger.warn(getName() + " putFile failed", e);
				throw new ProviderException(getName() + " putFile failed", e,
						item.getPath());
			} finally {
				easycloud.ecs.common.domain.Log log = new easycloud.ecs.common.domain.Log();
				log.setLogType(LOG_TYPE.PUT);
				log.setLength(length);
				log.setCreateTime(new Date());
				log.setResult(result);
				log.setProvider(config);
				sendLogService.addLog(log);
			}
		}
		return new AliyunStorageObjectMetadata(putResult, item);
	}

	@Override
	public boolean ready() {
		if (isReady) {
			return true;
		}

		try {
			isReady = AliyunOSSProvider.adapter
					.isBucketExist(AliyunOSSProvider.BUCKET_NAME);
		} catch (OSSException e) {
			AliyunOSSProvider.logger.warn(getName() + " init failed", e);
			isReady = false;
		} catch (ClientException e) {
			AliyunOSSProvider.logger.warn(getName() + " init failed", e);
			isReady = false;
		}
		return isReady;
	}

	@Override
	public void setSendLogService(SendLogService sendLogService) {
		this.sendLogService = sendLogService;
	}

}
